using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

namespace InputSystem
{
    public class InputManager : SingletonMonoBehaviour<InputManager>, GameInput.IKeyboardActions, IUpdateInputEvent
    {
        private GameInput _gameinputActions;
        /// <summary>
        /// the phase of the key input
        /// </summary>
        private List<InputActionPhase> _phaseFNList;
        private List<Action> ON_FN_Started;
        private List<Action> ON_FN_Performed;
        private List<Action> ON_FN_Canceled;

        private Action<Vector2> ON_Vector2FN;

        private void Awake()
        {
            INI_Singleton();

            _phaseFNList = new List<InputActionPhase>();
            ON_FN_Started = new List<Action>();
            ON_FN_Performed = new List<Action>();
            ON_FN_Canceled = new List<Action>();
            ON_Vector2FN = delegate (Vector2 vector2) { };
            for (int i = 0; i < KeyCounter.KeyCounts; i++)
            {
                _phaseFNList.Add(new InputActionPhase());
                ON_FN_Started.Add(delegate { });
                ON_FN_Performed.Add(delegate { });
                ON_FN_Canceled.Add(delegate { });
            }

            _gameinputActions = new GameInput();
        }

        private void Start()
        {
            _gameinputActions.Keyboard.SetCallbacks(this);
            _gameinputActions.Keyboard.Enable();
        }

        private void Update()
        {
            for (int i = 0; i < KeyCounter.KeyCounts; i++)
            {
                if (_phaseFNList[i] == InputActionPhase.Performed) ON_FN_Performed[i]();
            }
        }

        #region KeyboardInputEvent
        public void OnKB1_1(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.S, context.phase); }
        public void OnKB1_2(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.A, context.phase); }
        public void OnKB1_3(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.D, context.phase); }
        public void OnKB1_4(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.W, context.phase); }
        public void OnKB2_1(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.I, context.phase); }
        public void OnKB2_2(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.O, context.phase); }
        public void OnKB2_3(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.P, context.phase); }
        public void OnKB2_4(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.J, context.phase); }
        public void OnKB2_5(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.K, context.phase); }
        public void OnKB2_6(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.L, context.phase); }
        public void OnKB3_1(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.Z, context.phase); }
        public void OnKB3_2(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.X, context.phase); }
        public void OnKB3_3(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.C, context.phase); }
        public void OnKB3_4(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.V, context.phase); }
        public void OnKB4(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.Space, context.phase); }
        public void OnKB5_1(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.NUM1, context.phase); }
        public void OnKB5_2(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.NUM2, context.phase); }
        public void OnKB5_3(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.NUM3, context.phase); }
        public void OnKB5_4(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.NUM4, context.phase); }
        public void OnKB5_5(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.NUM5, context.phase); }
        public void OnKB6_1(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.Shift, context.phase); }
        public void OnKB6_2(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.Ctrl, context.phase); }
        public void OnKB6_3(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.Alt, context.phase); }
        public void OnKB6_4(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.BackSpace, context.phase); }
        public void OnKB6_5(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.Enter, context.phase); }
        public void OnKB6_6(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.Escape, context.phase); }
        public void OnKB6_7(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.Tab, context.phase); }
        public void OnKB7_1(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.DownArrow, context.phase); }
        public void OnKB7_2(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.LeftArrow, context.phase); }
        public void OnKB7_3(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.RightArrow, context.phase); }
        public void OnKB7_4(InputAction.CallbackContext context) { EventHandler(GameplayKeyCode.UpArrow, context.phase); }
        public void OnKB_Dir(InputAction.CallbackContext context) { ON_Vector2FN(context.ReadValue<Vector2>()); }
        #endregion

        private void EventHandler(GameplayKeyCode keyCode, InputActionPhase phase)
        {
            _phaseFNList[(int)keyCode] = phase;
            switch (phase)
            {
                case InputActionPhase.Started:
                    ON_FN_Started[(int)keyCode]();
                    break;
                case InputActionPhase.Canceled:
                    ON_FN_Canceled[(int)keyCode]();
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// register or unregister an event
        /// </summary>
        /// <param name="keyCode"></param>
        /// <param name="phase"></param>
        /// <param name="action"></param>
        /// <param name="isToCombineEvent"></param>
        public void UpdateInputEvent(GameplayKeyCode keyCode, InputActionPhase phase, Action action, bool isToCombineEvent)
        {
            if (isToCombineEvent)
            {
                switch (phase)
                {
                    case InputActionPhase.Started:
                        ON_FN_Started[(int)keyCode] += action;
                        break;
                    case InputActionPhase.Performed:
                        ON_FN_Performed[(int)keyCode] += action;
                        break;
                    case InputActionPhase.Canceled:
                        ON_FN_Canceled[(int)keyCode] += action;
                        break;
                    default:
                        break;
                }
            }
            else
            {
                switch (phase)
                {
                    case InputActionPhase.Started:
                        ON_FN_Started[(int)keyCode] -= action;
                        break;
                    case InputActionPhase.Performed:
                        ON_FN_Performed[(int)keyCode] -= action;
                        break;
                    case InputActionPhase.Canceled:
                        ON_FN_Canceled[(int)keyCode] -= action;
                        break;
                    default:
                        break;
                }
            }
        }

        /// <summary>
        /// vector event
        /// </summary>
        public void UpdateInputEvent(Action<Vector2> action, bool isToCombineEvent)
        {
            if (isToCombineEvent)
            {
                ON_Vector2FN += action;
            }
            else
            {
                ON_Vector2FN -= action;
            }
        }
    }
}